Conversation
🦋 Changeset detectedLatest commit: aa3c81f The changes in this PR will be included in the next version bump. This PR includes changesets to release 12 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds QR code collector support: new QR field/collector types and utils, reducer integration, UI component and e2e test for rendering QR images/fallbacks, server-config updates, CI tweaks, and a changeset for releasing davinci-client. Changes
Sequence Diagram(s)sequenceDiagram
participant Field as DaVinci Field (QR_CODE)
participant Reducer as Node Reducer
participant Collector as returnQrCodeCollector
participant UI as e2e QR Component
participant Form as HTMLFormElement
Field->>Reducer: dispatch node/next with field
Reducer->>Collector: returnQrCodeCollector(field, idx)
Collector-->>Reducer: QrCodeCollector (output, error)
Reducer-->>UI: renderer receives collector
UI->>Form: append image (collector.output.src) or error message and optional fallback text
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
View your CI Pipeline Execution ↗ for commit aa3c81f
☁️ Nx Cloud last updated this comment at |
Codecov Report✅ All modified and coverable lines are covered by tests. ❌ Your project status has failed because the head coverage (14.90%) is below the target coverage (40.00%). You can increase the head coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## main #562 +/- ##
===========================================
- Coverage 70.90% 14.90% -56.00%
===========================================
Files 53 153 +100
Lines 2021 26304 +24283
Branches 377 1071 +694
===========================================
+ Hits 1433 3921 +2488
- Misses 588 22383 +21795
🚀 New features to boost your workflow:
|
@forgerock/davinci-client
@forgerock/device-client
@forgerock/journey-client
@forgerock/oidc-client
@forgerock/protect
@forgerock/sdk-types
@forgerock/sdk-utilities
@forgerock/iframe-manager
@forgerock/sdk-logger
@forgerock/sdk-oidc
@forgerock/sdk-request-middleware
@forgerock/storage
commit: |
|
Deployed 72aac18 to https://ForgeRock.github.io/ping-javascript-sdk/pr-562/72aac181b67c54d4ecc86b1701222e573850f394 branch gh-pages in ForgeRock/ping-javascript-sdk |
📦 Bundle Size Analysis📦 Bundle Size Analysis🚨 Significant Changes🔻 @forgerock/device-client - 0.0 KB (-9.7 KB, -100.0%) 📊 Minor Changes📉 @forgerock/device-client - 9.7 KB (-0.0 KB) ➖ No Changes➖ @forgerock/sdk-utilities - 11.2 KB 14 packages analyzed • Baseline from latest Legend🆕 New package ℹ️ How bundle sizes are calculated
🔄 Updated automatically on each push to this PR |
a2633f1 to
65a6b01
Compare
There was a problem hiding this comment.
🧹 Nitpick comments (1)
e2e/davinci-app/components/qr-code.ts (1)
10-14: Consider adding adata-testidto the error element for e2e test coverage.This would enable testing error scenarios in automated tests.
🧪 Proposed improvement
if (collector.error) { const errorEl = document.createElement('p'); errorEl.innerText = `QR Code error: ${collector.error}`; + errorEl.setAttribute('data-testid', 'qr-code-error'); formEl.appendChild(errorEl); return; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@e2e/davinci-app/components/qr-code.ts` around lines 10 - 14, Add a data-testid attribute to the error paragraph so e2e tests can select it: when handling collector.error in the QR code component (the branch that creates errorEl and appends to formEl), set errorEl.dataset.testid = 'qr-code-error' (or use errorEl.setAttribute('data-testid', 'qr-code-error')) before appending; keep the existing innerText and return behavior unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@e2e/davinci-app/components/qr-code.ts`:
- Around line 10-14: Add a data-testid attribute to the error paragraph so e2e
tests can select it: when handling collector.error in the QR code component (the
branch that creates errorEl and appends to formEl), set errorEl.dataset.testid =
'qr-code-error' (or use errorEl.setAttribute('data-testid', 'qr-code-error'))
before appending; keep the existing innerText and return behavior unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 554b1372-8058-424c-9dda-d435ac4bab4a
📒 Files selected for processing (12)
e2e/davinci-app/components/qr-code.tse2e/davinci-app/main.tse2e/davinci-app/server-configs.tspackages/davinci-client/src/lib/collector.types.tspackages/davinci-client/src/lib/collector.utils.test.tspackages/davinci-client/src/lib/collector.utils.tspackages/davinci-client/src/lib/davinci.types.tspackages/davinci-client/src/lib/node.reducer.test.tspackages/davinci-client/src/lib/node.reducer.tspackages/davinci-client/src/lib/node.types.test-d.tspackages/davinci-client/src/lib/node.types.tspackages/davinci-client/src/types.ts
There was a problem hiding this comment.
Nx Cloud has identified a flaky task in your failed CI:
🔂 Since the failure was identified as flaky, we triggered a CI rerun by adding an empty commit to this branch.
🔔 Heads up, your workspace has pending recommendations ↗ to auto-apply fixes for similar failures.
🎓 Learn more about Self-Healing CI on nx.dev
| * @param {number} idx - The index to be used in the id of the QrCodeCollector. | ||
| * @returns {QrCodeCollectorBase} The constructed QrCodeCollector object. | ||
| */ | ||
| export function returnQrCodeCollector(field: QrCodeField, idx: number): QrCodeCollectorBase { |
There was a problem hiding this comment.
Can we reuse the generic returnNoValueCollector function? I feel like we can change around the properties a bit to make it fit. It feels like fallbackText should be the label and we just need to introduce a new prop for the src.
There was a problem hiding this comment.
🧹 Nitpick comments (2)
e2e/davinci-app/components/qr-code.ts (1)
20-23: Validate QR image payload format before assigningimg.src.Line 20 trusts
collector.output.srcdirectly; add a data-URI guard so malformed/hosted URLs are rejected explicitly.Proposed hardening
+ const isDataImageUri = /^data:image\/[a-zA-Z+.-]+;base64,/.test(collector.output.src); + if (!isDataImageUri) { + const errorEl = document.createElement('p'); + errorEl.innerText = 'QR Code error: invalid QR image payload'; + formEl.appendChild(errorEl); + return; + } + const img = document.createElement('img'); img.src = collector.output.src;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@e2e/davinci-app/components/qr-code.ts` around lines 20 - 23, Validate that collector.output.src is a proper data URI before assigning it to img.src: in the QR component where img.src = collector.output.src is set, add a guard that checks the string starts with "data:image/" and matches a data URI pattern (e.g., /^data:image\/[a-zA-Z]+;base64,/) and only then assign to img.src; if validation fails, do not set img.src and instead set a safe fallback (e.g., leave alt text, set a placeholder src or log/emit an error) prior to container.appendChild(img) so malformed or hosted URLs are explicitly rejected.e2e/davinci-suites/src/qr-code.test.ts (1)
21-21: Replace genericrequestfinishedwaits with state-specific assertions.Lines 21, 26, 30, and 35 can be flaky because any request completion may satisfy them. Wait for the next expected UI state (or specific response URL) after each action.
Also applies to: 26-26, 30-30, 35-35
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@e2e/davinci-suites/src/qr-code.test.ts` at line 21, Replace the flaky page.waitForEvent('requestfinished') calls with state-specific waits: after each action that currently uses page.waitForEvent('requestfinished') (instances of page.waitForEvent('requestfinished') in the test), wait for the specific network response using page.waitForResponse(resp => resp.url().includes('<expected-path-or-endpoint>') && resp.status() === 200) or wait for the expected UI state via await page.waitForSelector('<expected-selector>', { state: 'visible' }) (choose the response URL or selector that matches the next UI change for each of the four occurrences).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@e2e/davinci-app/components/qr-code.ts`:
- Around line 20-23: Validate that collector.output.src is a proper data URI
before assigning it to img.src: in the QR component where img.src =
collector.output.src is set, add a guard that checks the string starts with
"data:image/" and matches a data URI pattern (e.g.,
/^data:image\/[a-zA-Z]+;base64,/) and only then assign to img.src; if validation
fails, do not set img.src and instead set a safe fallback (e.g., leave alt text,
set a placeholder src or log/emit an error) prior to container.appendChild(img)
so malformed or hosted URLs are explicitly rejected.
In `@e2e/davinci-suites/src/qr-code.test.ts`:
- Line 21: Replace the flaky page.waitForEvent('requestfinished') calls with
state-specific waits: after each action that currently uses
page.waitForEvent('requestfinished') (instances of
page.waitForEvent('requestfinished') in the test), wait for the specific network
response using page.waitForResponse(resp =>
resp.url().includes('<expected-path-or-endpoint>') && resp.status() === 200) or
wait for the expected UI state via await
page.waitForSelector('<expected-selector>', { state: 'visible' }) (choose the
response URL or selector that matches the next UI change for each of the four
occurrences).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 1480b882-5109-4835-be79-54a3386905ba
📒 Files selected for processing (14)
.changeset/fast-ways-rest.mde2e/davinci-app/components/qr-code.tse2e/davinci-app/main.tse2e/davinci-app/server-configs.tse2e/davinci-suites/src/qr-code.test.tspackages/davinci-client/src/lib/collector.types.tspackages/davinci-client/src/lib/collector.utils.test.tspackages/davinci-client/src/lib/collector.utils.tspackages/davinci-client/src/lib/davinci.types.tspackages/davinci-client/src/lib/node.reducer.test.tspackages/davinci-client/src/lib/node.reducer.tspackages/davinci-client/src/lib/node.types.test-d.tspackages/davinci-client/src/lib/node.types.tspackages/davinci-client/src/types.ts
✅ Files skipped from review due to trivial changes (5)
- .changeset/fast-ways-rest.md
- packages/davinci-client/src/types.ts
- packages/davinci-client/src/lib/node.types.test-d.ts
- packages/davinci-client/src/lib/collector.utils.test.ts
- packages/davinci-client/src/lib/node.types.ts
🚧 Files skipped from review as they are similar to previous changes (5)
- e2e/davinci-app/main.ts
- packages/davinci-client/src/lib/node.reducer.ts
- packages/davinci-client/src/lib/collector.utils.ts
- packages/davinci-client/src/lib/node.reducer.test.ts
- packages/davinci-client/src/lib/collector.types.ts
There was a problem hiding this comment.
🧹 Nitpick comments (2)
e2e/davinci-suites/src/qr-code.test.ts (2)
47-52: Conditional fallback assertion may silently pass if fallback is missing.The conditional
if (fallbackVisible)means the test will pass even if the fallback element is never rendered. If the fallback text is expected behavior for this flow (the PR screenshot shows "04AMFGOEWFENIB62O" as a manual code), consider making this assertion unconditional:const fallback = page.locator('[data-testid="qr-code-fallback"]'); - const fallbackVisible = await fallback.isVisible(); - if (fallbackVisible) { - const fallbackText = await fallback.textContent(); - expect(fallbackText).toBeTruthy(); - } + await expect(fallback).toBeVisible(); + const fallbackText = await fallback.textContent(); + expect(fallbackText).toBeTruthy();If the fallback is truly optional depending on the flow configuration, consider adding a comment explaining when it's expected vs. optional.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@e2e/davinci-suites/src/qr-code.test.ts` around lines 47 - 52, The current conditional check around the QR fallback allows the test to silently pass when the fallback element is not present; update the test so that the fallback text assertion is unconditional (remove the `if (fallbackVisible)` guard) and directly assert the fallback element is visible and has non-empty text using the existing `fallback`, `fallbackVisible`/`fallbackText` variables (e.g., ensure you first assert `fallback.isVisible()` then `fallback.textContent()` is truthy), or if the fallback is legitimately optional add a clear comment explaining when it is expected and keep the conditional but assert visibility state explicitly.
20-26: Consider more targeted request waiting to reduce flakiness.Using
page.waitForEvent('requestfinished')will resolve on any completed network request, not necessarily the one triggered by the preceding action. If the page has background requests (analytics, polling, etc.), this could introduce intermittent failures or false positives.Consider using
page.waitForResponse()with a URL pattern, or wrapping the click + wait inPromise.all:await Promise.all([ page.waitForResponse((resp) => resp.url().includes('/expected-endpoint') && resp.ok()), page.getByRole('button', { name: 'USER_LOGIN' }).click(), ]);This pattern is more resilient and explicitly ties the wait to the expected request.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@e2e/davinci-suites/src/qr-code.test.ts` around lines 20 - 26, Replace the broad page.waitForEvent('requestfinished') calls around the login actions with targeted waits tied to the expected network request(s): wrap the click on page.getByRole('button', { name: 'USER_LOGIN' }).click() and the Sign On click in Promise.all paired with page.waitForResponse or page.waitForRequest using a URL predicate that matches the login endpoint (e.g., resp.url().includes('/expected-endpoint') && resp.ok()), and do the same for the second click on page.getByRole('button', { name: 'Sign On' }).click() so the test explicitly waits for the correct request/response instead of any finished network request.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@e2e/davinci-suites/src/qr-code.test.ts`:
- Around line 47-52: The current conditional check around the QR fallback allows
the test to silently pass when the fallback element is not present; update the
test so that the fallback text assertion is unconditional (remove the `if
(fallbackVisible)` guard) and directly assert the fallback element is visible
and has non-empty text using the existing `fallback`,
`fallbackVisible`/`fallbackText` variables (e.g., ensure you first assert
`fallback.isVisible()` then `fallback.textContent()` is truthy), or if the
fallback is legitimately optional add a clear comment explaining when it is
expected and keep the conditional but assert visibility state explicitly.
- Around line 20-26: Replace the broad page.waitForEvent('requestfinished')
calls around the login actions with targeted waits tied to the expected network
request(s): wrap the click on page.getByRole('button', { name: 'USER_LOGIN'
}).click() and the Sign On click in Promise.all paired with page.waitForResponse
or page.waitForRequest using a URL predicate that matches the login endpoint
(e.g., resp.url().includes('/expected-endpoint') && resp.ok()), and do the same
for the second click on page.getByRole('button', { name: 'Sign On' }).click() so
the test explicitly waits for the correct request/response instead of any
finished network request.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 22d283af-6079-4f32-983d-3fc1170d4352
📒 Files selected for processing (3)
.github/workflows/ci.ymle2e/davinci-suites/src/qr-code.test.tspackages/davinci-client/src/lib/collector.utils.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- packages/davinci-client/src/lib/collector.utils.ts
d8af5c3 to
213bd20
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@e2e/davinci-suites/src/qr-code.test.ts`:
- Around line 49-52: The current assertion uses fallbackVisible and
fallback.textContent() with toBeTruthy(), which allows responses like "Manual
code: " to pass; instead read the text via fallback.textContent(), remove the
known prefix (e.g., "Manual code:"), trim the remainder, and assert that the
trimmed string is non-empty (or matches /\S/). Update the test around the
fallbackVisible branch to compute const text = await fallback.textContent(),
strip the prefix and whitespace, then
expect(theStrippedText.length).toBeGreaterThan(0) (or
expect(theStrippedText).toMatch(/\S/)), referencing the existing fallbackVisible
and fallback variables.
- Around line 20-35: The unscoped page.waitForEvent('requestfinished') calls
after clicks (seen around page.getByRole(..., { name: 'USER_LOGIN' }),
page.getByRole(..., { name: 'Sign On' }), page.getByRole(..., { name:
'DEVICE_REGISTRATION' }), and page.getByRole(..., { name: 'Mobile App' })) are
racy; replace them with either (a) web-first UI assertions that auto-retry (e.g.
await expect(page.getByText('MFA Device Selection -
Registration')).toBeVisible() or expecting a navigation/URL change via
toHaveURL) or (b) scoped network waits that start before the action (use
Promise.all with page.waitForResponse(resp =>
resp.url().includes('<unique-path>') && resp.status() === 200) and the
.click()), matching the specific request path for the LOGIN, Sign On,
DEVICE_REGISTRATION and Mobile App actions instead of waiting for any finished
request.
In `@packages/davinci-client/src/lib/collector.utils.ts`:
- Around line 732-739: The returned object currently spreads base.output into
output, but base.output.label can be a non-string when server payloads are
malformed; update the output normalization in the return of the function (the
object that merges ...base and output: {...base.output, src: field.content ||
'', fallbackText: field.fallbackText || ''}) to explicitly normalize label to a
string (e.g., set label to base.output.label if typeof base.output.label ===
'string' else ''), so QrCodeCollectorBase's runtime shape guarantee is
preserved.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 86df6553-a839-4b3a-af53-15d5df2817de
📒 Files selected for processing (3)
.github/workflows/ci.ymle2e/davinci-suites/src/qr-code.test.tspackages/davinci-client/src/lib/collector.utils.ts
✅ Files skipped from review due to trivial changes (1)
- .github/workflows/ci.yml
| await page.getByRole('button', { name: 'USER_LOGIN' }).click(); | ||
| await page.waitForEvent('requestfinished'); | ||
|
|
||
| await page.getByLabel('Username').fill(username); | ||
| await page.getByLabel('Password').fill(password); | ||
| await page.getByRole('button', { name: 'Sign On' }).click(); | ||
| await page.waitForEvent('requestfinished'); | ||
|
|
||
| // Step 2: Select device registration | ||
| await page.getByRole('button', { name: 'DEVICE_REGISTRATION' }).click(); | ||
| await page.waitForEvent('requestfinished'); | ||
|
|
||
| // Step 3: Choose "Mobile App" from the device selection screen | ||
| await expect(page.getByText('MFA Device Selection - Registration')).toBeVisible(); | ||
| await page.getByRole('button', { name: 'Mobile App' }).click(); | ||
| await page.waitForEvent('requestfinished'); |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In Playwright, is using page.waitForEvent('requestfinished') after a click considered flaky, and what are the recommended alternatives for deterministic synchronization in E2E tests?
💡 Result:
Yes—page.waitForEvent('requestfinished') after a click() is often flaky/racy in E2E tests because:
requestfinishedfires for any request that completes (including redirects and even HTTP error responses like 404/503), so you can easily synchronize on the “wrong” request when multiple requests are in flight. [1]- If you start waiting after the click, you can miss fast network activity and then hang until timeout (classic race pattern).
Recommended deterministic alternatives
-
Wait for a specific response (preferred when an API call is the real “done” signal)
Register the wait before the click and match by URL/predicate:const respPromise = page.waitForResponse(r => r.url().includes('/api/checkout') && r.status() === 200 ); await page.getByRole('button', { name: 'Buy' }).click(); await respPromise;
-
Wait for URL / navigation deterministically (preferred when the user-visible result is navigation)
Usepage.waitForURL()(Playwright explicitly recommends URL waiting patterns for navigation sync). [3]
Typical race-safe pattern:await Promise.all([ page.waitForURL('**/order/confirmation'), page.getByRole('button', { name: 'Buy' }).click(), ]);
-
Wait on user-visible UI state via “web-first” assertions (preferred for true E2E behavior)
Playwright recommends asserting what the user sees; these assertions auto-wait/retry until the condition is met. [2]await page.getByRole('button', { name: 'Buy' }).click(); await expect(page.getByText('Order confirmed')).toBeVisible();
-
If you must sync with network, target the request/response—not “any finished request”
PreferwaitForResponse/waitForRequestwith a predicate overrequestfinished(which is broad and easy to mismatch). [1]
Source list
[1] Playwright docs: request event order and meaning of requestfinished (incl. redirects and HTTP error responses still ending in requestfinished). (playwright.dev)
[2] Playwright docs: Best Practices—test user-visible behavior; use web-first assertions that auto-wait. (playwright.dev)
[3] Playwright docs: Navigations—“Waiting for navigation” using page.waitForURL(). (playwright.dev)
Citations:
- 1: https://playwright.dev/docs/api/class-request
- 2: https://playwright.dev/docs/best-practices
- 3: https://playwright.dev/docs/next/navigations?utm_source=openai
Unscoped requestfinished waits introduce race conditions and test flakiness.
On lines 21, 26, 30, and 35, page.waitForEvent('requestfinished') resolves on any finished request (including unrelated redirects and HTTP error responses). Starting the wait after the click creates a classic race: if the network activity completes before the listener is registered, the test will hang until timeout.
Replace with UI-state waits (web-first assertions that auto-retry) or scoped response waits matched by URL:
Stabilization pattern
- await page.getByRole('button', { name: 'USER_LOGIN' }).click();
- await page.waitForEvent('requestfinished');
+ await page.getByRole('button', { name: 'USER_LOGIN' }).click();
+ await expect(page.getByLabel('Username')).toBeVisible();
await page.getByLabel('Username').fill(username);
await page.getByLabel('Password').fill(password);
- await page.getByRole('button', { name: 'Sign On' }).click();
- await page.waitForEvent('requestfinished');
+ await page.getByRole('button', { name: 'Sign On' }).click();
+ await expect(page.getByRole('button', { name: 'DEVICE_REGISTRATION' })).toBeVisible();
- await page.getByRole('button', { name: 'DEVICE_REGISTRATION' }).click();
- await page.waitForEvent('requestfinished');
+ await page.getByRole('button', { name: 'DEVICE_REGISTRATION' }).click();
+ await expect(page.getByText('MFA Device Selection - Registration')).toBeVisible();
await expect(page.getByText('MFA Device Selection - Registration')).toBeVisible();
- await page.getByRole('button', { name: 'Mobile App' }).click();
- await page.waitForEvent('requestfinished');
+ await page.getByRole('button', { name: 'Mobile App' }).click();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@e2e/davinci-suites/src/qr-code.test.ts` around lines 20 - 35, The unscoped
page.waitForEvent('requestfinished') calls after clicks (seen around
page.getByRole(..., { name: 'USER_LOGIN' }), page.getByRole(..., { name: 'Sign
On' }), page.getByRole(..., { name: 'DEVICE_REGISTRATION' }), and
page.getByRole(..., { name: 'Mobile App' })) are racy; replace them with either
(a) web-first UI assertions that auto-retry (e.g. await
expect(page.getByText('MFA Device Selection - Registration')).toBeVisible() or
expecting a navigation/URL change via toHaveURL) or (b) scoped network waits
that start before the action (use Promise.all with page.waitForResponse(resp =>
resp.url().includes('<unique-path>') && resp.status() === 200) and the
.click()), matching the specific request path for the LOGIN, Sign On,
DEVICE_REGISTRATION and Mobile App actions instead of waiting for any finished
request.
| if (fallbackVisible) { | ||
| const fallbackText = await fallback.textContent(); | ||
| expect(fallbackText).toBeTruthy(); | ||
| } |
There was a problem hiding this comment.
Strengthen fallback-text assertion to validate actual code content.
toBeTruthy() can still pass for "Manual code: " without a meaningful code. Consider asserting non-empty trimmed content after removing the prefix.
Suggested assertion tweak
if (fallbackVisible) {
- const fallbackText = await fallback.textContent();
- expect(fallbackText).toBeTruthy();
+ const fallbackText = (await fallback.textContent()) ?? '';
+ const manualCode = fallbackText.replace(/^Manual code:\s*/i, '').trim();
+ expect(manualCode).not.toEqual('');
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@e2e/davinci-suites/src/qr-code.test.ts` around lines 49 - 52, The current
assertion uses fallbackVisible and fallback.textContent() with toBeTruthy(),
which allows responses like "Manual code: " to pass; instead read the text via
fallback.textContent(), remove the known prefix (e.g., "Manual code:"), trim the
remainder, and assert that the trimmed string is non-empty (or matches /\S/).
Update the test around the fallbackVisible branch to compute const text = await
fallback.textContent(), strip the prefix and whitespace, then
expect(theStrippedText.length).toBeGreaterThan(0) (or
expect(theStrippedText).toMatch(/\S/)), referencing the existing fallbackVisible
and fallback variables.
| return { | ||
| ...base, | ||
| error: error || null, | ||
| output: { | ||
| ...base.output, | ||
| src: field.content || '', | ||
| fallbackText: field.fallbackText || '', | ||
| }, |
There was a problem hiding this comment.
Normalize output.label for malformed QR payloads.
On Line 736, label is inherited from base.output and can be non-string at runtime when server payloads are malformed. This breaks the QrCodeCollectorBase runtime shape guarantee. Normalize it to '' the same way src/fallbackText are normalized.
Suggested patch
return {
...base,
error: error || null,
output: {
...base.output,
+ label: typeof base.output.label === 'string' ? base.output.label : '',
src: field.content || '',
fallbackText: field.fallbackText || '',
},
};📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| return { | |
| ...base, | |
| error: error || null, | |
| output: { | |
| ...base.output, | |
| src: field.content || '', | |
| fallbackText: field.fallbackText || '', | |
| }, | |
| return { | |
| ...base, | |
| error: error || null, | |
| output: { | |
| ...base.output, | |
| label: typeof base.output.label === 'string' ? base.output.label : '', | |
| src: field.content || '', | |
| fallbackText: field.fallbackText || '', | |
| }, | |
| }; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/davinci-client/src/lib/collector.utils.ts` around lines 732 - 739,
The returned object currently spreads base.output into output, but
base.output.label can be a non-string when server payloads are malformed; update
the output normalization in the return of the function (the object that merges
...base and output: {...base.output, src: field.content || '', fallbackText:
field.fallbackText || ''}) to explicitly normalize label to a string (e.g., set
label to base.output.label if typeof base.output.label === 'string' else ''), so
QrCodeCollectorBase's runtime shape guarantee is preserved.
Add QR_CODE field type support to the DaVinci client SDK, enabling applications to render QR codes returned by DaVinci flows. - Add QrCodeField type and QrCodeCollectorBase interface - Add returnQrCodeCollector factory with defensive fallbacks - Wire QR_CODE dispatch in node reducer (early return, same as LABEL) - Export QrCodeCollector in public API types - Add QR code component and config to sample davinci-app - Add unit tests for factory and reducer integration
Jira
https://pingidentity.atlassian.net/browse/SDKS-4686
Summary
QR_CODEfield type support to the DaVinci client SDK, enabling applications to render QR codes returned by DaVinci flowsQrCodeCollector(NoValueCollector family) withoutput.src(base64 data URI) andoutput.fallbackText(manual pairing key)QR_CODEfield dispatch in the node reducer with defensive fallbacks for malformed server responsesChanges
davinci-client
davinci.types.ts— NewQrCodeFieldtype (type: 'QR_CODE',key,content,fallbackText?)collector.types.ts— NewQrCodeCollectorBaseinterface withsrcandfallbackTextin outputcollector.utils.ts—returnQrCodeCollectorfactory with defensive fallbacks matchingreturnNoValueCollectorpatternnode.reducer.ts—QR_CODEearly return before switch (same pattern asLABEL)node.types.ts/types.ts—QrCodeCollectorregistered inCollectorsunion and public APIdavinci-app (sample)
components/qr-code.ts— Renders QR code<img>with fallback text, checkscollector.errormain.ts— WiresQrCodeCollectorbranch into render loopserver-configs.ts— Adds QR code flow test configTests
returnQrCodeCollector(happy path, missing fallbackText, missing content, missing key, accumulated errors)QR_CODEfield dispatchCollectorsunionTest plan
nx run @forgerock/davinci-client:nxTest)http://localhost:5829/?clientId=c12743f9-08e8-4420-a624-71bbb08e9fe1and verify QR code rendersSummary by CodeRabbit
New Features
Tests
Chores